package com.scenic.rd_android.threerd.rxjava_retrofit;

import android.support.annotation.Nullable;

import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.StringReader;
import java.io.Writer;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.util.concurrent.TimeUnit;

import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import okio.Buffer;
import retrofit2.Converter;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;

import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
 * Created by scenic on 16/9/1.
 */
public class ServiceFactory {



    public static Converter.Factory getConvertFactory() {
//        return GsonConverterFactory.create();
        return new ConvertFactory();
    }

    static final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
        private final Gson gson;
        private final TypeAdapter<T> adapter;

        GsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
            this.gson = gson;
            this.adapter = adapter;
        }

        @Override
        public T convert(ResponseBody value) throws IOException {

            byte[] bytes = value.bytes();
            String jsonString = new String(bytes);
            System.out.println(jsonString);

            JsonReader jsonReader = gson.newJsonReader(new StringReader(jsonString));
            T t = adapter.read(jsonReader);

            try {
                return t;
            } finally {
                value.close();
            }
        }
    }

    static final class GsonRequestBodyConverter<T> implements Converter<T, RequestBody> {
        private static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=UTF-8");
        private static final Charset UTF_8 = Charset.forName("UTF-8");

        private final Gson gson;
        private final TypeAdapter<T> adapter;

        GsonRequestBodyConverter(Gson gson, TypeAdapter<T> adapter) {
            this.gson = gson;
            this.adapter = adapter;
        }

        @Override
        public RequestBody convert(T value) throws IOException {
            Buffer buffer = new Buffer();
            Writer writer = new OutputStreamWriter(buffer.outputStream(), UTF_8);
            JsonWriter jsonWriter = gson.newJsonWriter(writer);
            adapter.write(jsonWriter, value);
            jsonWriter.close();
            return RequestBody.create(MEDIA_TYPE, buffer.readByteString());
        }
    }


    static final class ConvertFactory extends Converter.Factory {
        Gson gson = new Gson();

        @Override
        public Converter<ResponseBody, ?> responseBodyConverter(Type type,
                                                                Annotation[] annotations,
                                                                Retrofit retrofit) {
            TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
            return new GsonResponseBodyConverter<>(gson, adapter);
        }

        @Override
        public Converter<?, RequestBody> requestBodyConverter(Type type,
                                                              Annotation[] parameterAnnotations,
                                                              Annotation[] methodAnnotations,
                                                              Retrofit retrofit) {
            TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
            return new GsonRequestBodyConverter<>(gson, adapter);
        }
    }


    public static <T> T create(Class<T> serviceClass) {
        Retrofit.Builder builder = new Retrofit.Builder();

        String baseUrl = findBaseUrl(serviceClass);

        //"http://localhost:6080"
        builder.baseUrl(baseUrl)
                .client(getOkHttpClient())
                .addConverterFactory(getConvertFactory())
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
        ;


        return builder.build().create(serviceClass);
    }

    @Nullable
    private static <T> String findBaseUrl(Class<T> serviceClass) {
        String baseUrl = null;

        Field[] fields = serviceClass.getDeclaredFields();
        for (Field f : fields) {
            f.setAccessible(true);

            Annotation[] annotations = f.getDeclaredAnnotations();
            for (Annotation a : annotations) {
                if (a.annotationType().equals(BaseUrl.class)) {
                    try {
                        baseUrl = (String) f.get(serviceClass);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                    break;
                }
            }
        }
        return baseUrl;
    }

    private final static long DEFAULT_TIMEOUT = 10;

    public static OkHttpClient getOkHttpClient() {
        //定制OkHttp
        OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
        //设置超时时间
        httpClientBuilder.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
        httpClientBuilder.writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
        httpClientBuilder.readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
        //设置缓存
//            File httpCacheDirectory = new File(FileUtils.getCacheDir(SolidApplication.getInstance()), "OkHttpCache");
//            httpClientBuilder.cache(new Cache(httpCacheDirectory, 10 * 1024 * 1024));

        return httpClientBuilder.build();
    }


    @Documented
    @Target(ElementType.FIELD)
    @Retention(RUNTIME)
    public @interface BaseUrl {
        String name() default "";
    }

}
